package pl.edu.agh.jemo.quicktests;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import pl.edu.agh.jemo.evolution.algorithm.AbstractAlgorithm;
import pl.edu.agh.jemo.evolution.algorithm.impl.SPEA2Algorithm;
import pl.edu.agh.jemo.evolution.genotype.impl.DoubleGenotype;
import pl.edu.agh.jemo.evolution.metrics.Metric;
import pl.edu.agh.jemo.evolution.objfunc.CrowdingObjectiveFunction;
import pl.edu.agh.jemo.evolution.objfunc.ObjectiveFunction;
import pl.edu.agh.jemo.evolution.objfunc.ObjectiveFunctionSet;
import pl.edu.agh.jemo.evolution.objfunc.impl.FitnessSharingObjectiveFunction;
import pl.edu.agh.jemo.evolution.objfunc.impl.MichalewiczObjFunc;
import pl.edu.agh.jemo.evolution.objfunc.impl.PopulationCentroidObjectiveFunction;
import pl.edu.agh.jemo.evolution.objfunc.impl.weighted.WeightedCrowdingObjectiveFunction;
import pl.edu.agh.jemo.evolution.operator.common.DomainControl;
import pl.edu.agh.jemo.evolution.operator.crossover.Crossover;
import pl.edu.agh.jemo.evolution.operator.crossover.impl.Radial2DCrossover;
import pl.edu.agh.jemo.evolution.operator.mutation.Mutation;
import pl.edu.agh.jemo.evolution.operator.mutation.impl.BalancedMutation;
import pl.edu.agh.jemo.evolution.population.Population;
import pl.edu.agh.jemo.evolution.selections.Tournament;
import pl.edu.agh.jemo.evolution.selections.impl.ClassicTournament;
import pl.edu.agh.jemo.evolution.specimen.impl.SPEA2Specimen;

public class TestAutomaton {
	/**
	 * NA POCZATEK 1. zrobic ustawianie mutacji i crossovero 2. funkcja crowding
	 * moze zalezec od funkcji multimodalnej 3. metryki i rysowanie wykresow
	 * zalezy od obu funkcji 4. algorytm powinien byc odpalony N razy dla danego
	 * przypadku 5. wyniki kazdej kombinacji musza odkladac sie w innym folderze
	 * i numerowac normalnie, a nie z jakimis dziwacznymi datami
	 * 
	 * OSOBNO napisac cos, co przeleci wszystkie wyniki metryk, wyciagnie
	 * srednie i spowoduje narysowanie wykresow
	 */
	private List<AbstractAlgorithm> algorithms = new ArrayList<AbstractAlgorithm>();
	private List<ObjectiveFunction> multimodalFunctions = new ArrayList<ObjectiveFunction>();
	private List<CrowdingObjectiveFunction> crowdingFunctions = new ArrayList<CrowdingObjectiveFunction>();
	private Map<String, List<Metric>> metricsByMultimodalFunction = new HashMap<String, List<Metric>>();

	private Mutation mutation;
	private Crossover crossover;
	private Tournament tournament;

	private int caseRunCount = 1;

	public void run() throws IOException {
		for (final ObjectiveFunction objFunc : multimodalFunctions) {
			for (final CrowdingObjectiveFunction crObjFunc : crowdingFunctions) {
				for (final AbstractAlgorithm algorithm : algorithms) {
					for (int i = 1; i <= caseRunCount; i++) {
						final String caseDir = "\\"
								+ objFunc.getClass().getSimpleName() + "_"
								+ crObjFunc.getClass().getSimpleName() + "_"
								+ algorithm.getClass().getSimpleName() + "_" + i + "\\";
						final ObjectiveFunctionSet objFuncSet = new ObjectiveFunctionSet();
						objFuncSet.add(objFunc);
						objFuncSet.add(crObjFunc);
						algorithm.setObjectiveFunctionSet(objFuncSet);
						algorithm.setMutation(getMutation());
						algorithm.setCrossover(getCrossover());
						algorithm.setTournament(getTournament());
						algorithm.setGenotypeType(DoubleGenotype.class);
						algorithm.runWithSpecifiedPlotPath(caseDir);
					}
				}
			}
		}
	}

	public Mutation getMutation() {
		return mutation;
	}

	public void setMutation(Mutation mutation) {
		this.mutation = mutation;
	}

	public Crossover getCrossover() {
		return crossover;
	}

	public void setCrossover(Crossover crossover) {
		this.crossover = crossover;
	}

	public Tournament getTournament() {
		return tournament;
	}

	public void setTournament(Tournament tournament) {
		this.tournament = tournament;
	}

	public void addAlgorithm(final AbstractAlgorithm algorithm) {
		this.algorithms.add(algorithm);
	}

	public void addMultimodalFunction(final ObjectiveFunction objFunc) {
		this.multimodalFunctions.add(objFunc);
	}

	public void addCrowdingObjectiveFunction(
			final CrowdingObjectiveFunction crObjFunc) {
		this.crowdingFunctions.add(crObjFunc);
	}

	public static void main(String[] args) throws Exception {
		final TestAutomaton ta = new TestAutomaton();

		BalancedMutation mutation = new BalancedMutation();
		mutation.setDomainControl(DomainControl.GENERATE_NEXT);
		mutation.setImproveSpecimen(false);
		mutation.setMutationChance(0.15);
		mutation.setStrongMutationChance(0.05);
		ta.setMutation(mutation);

		Radial2DCrossover crossover = new Radial2DCrossover();
		crossover.setCrossoverChance(0.15);
		crossover.setDomainControl(DomainControl.GENERATE_NEXT);
		crossover.setImproveSpecimen(false);
		ta.setCrossover(crossover);

		ClassicTournament tournament = new ClassicTournament();
		tournament.setExpectedPopulationSize(900);
		tournament.setProbability(.8);
		tournament.setTournamentSizeRatio(.8);
		ta.setTournament(tournament);

		MichalewiczObjFunc michalewicz = new MichalewiczObjFunc();
		michalewicz.setM(2.);
		ta.addMultimodalFunction(michalewicz);
		FitnessSharingObjectiveFunction fun = new FitnessSharingObjectiveFunction();
		fun.setNicheRadius(.38);
		//ta.addCrowdingObjectiveFunction(fun);
		PopulationCentroidObjectiveFunction popCenFun = new PopulationCentroidObjectiveFunction();
		//ta.addCrowdingObjectiveFunction(popCenFun);
		WeightedCrowdingObjectiveFunction wco = new WeightedCrowdingObjectiveFunction(
				popCenFun, michalewicz, 3d);
		ta.addCrowdingObjectiveFunction(wco);

		Population population = new Population();

		SPEA2Algorithm algorithm = new SPEA2Algorithm();
		algorithm.setArchiveSize(300);
		algorithm.setSpecimenType(SPEA2Specimen.class);
		algorithm.setGenotypeType(DoubleGenotype.class);
		algorithm.setHillClimbingMultiplier(0d);
		algorithm.setPopulation(population);
		algorithm.setPopulationSize(900);
		algorithm.setSteps(200);
		ta.addAlgorithm(algorithm);

		ta.setCaseRunCount(1);
		
		ta.run();
	}

	public void setCaseRunCount(int caseRunCount) {
		this.caseRunCount = caseRunCount;
	}

	public int getCaseRunCount() {
		return caseRunCount;
	}

}
